#ifndef _BTL_EXTERNAL_TEMPLATE_STORAGE_H
#define _BTL_EXTERNAL_TEMPLATE_STORAGE_H
#include <stdint.h>
#include <stdlib.h>

#include "btl_bep.h"

enum BTL_STORAGE_ERR_CODE {
    BSEC_OK            = 0,           // 成功
    BSEC_OUT_OF_MEMORY = BTL_RC_MAX,  // 内存不足
    BSEC_WRONG_PARAM,                 // 输入参数不正确
    BSEC_INVALID_TEMPLATE,            // 无效模板
    BSEC_CHECK_SUM,                   // 校验失败
    BSEC_TEMPLATE_EMPTY,              // 不存在有效模板
    BSEC_MISS_ID,                     // 不存在该ID的模板
    BSEC_NO_USABLE_ID,                // 没有可用ID
    BSEC_NO_USABLE_SPACE,             // 没有可用空间
    BSEC_TEMPLATE_TOO_LARGE,          // 模板太大
    BSEC_UNDEFINE_EXTERNAL_API,       // 外部接口未实现
    BSEC_TOO_MUCH_UPDATE,             // 更新次数过多
    BSEC_WRITE_FLASH_FAIL,            // 写flash失败
    BSEC_DECRYPT_FAIL,                // 解密失败
};

/* @name btl_crypto_init0
*  @brief 初始化默认加密算法 AES-ECB 128bit
*  @param key[16] 传入16-bytes key
*  @example
       unsigned char key[16] = {
           0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff,
           0x08, 0x09, 0x10, 0x10, 0x00, 0x00, 0xff, 0xff,
       };
       btl_crypto_init0(key);
*  @note 1. 默认软件算法支持AES-ECB 128bit 算法
*  @note 2. 如果key，非全零则使能加解密，全零则不使能加解密算法
*  @note 3. btl_crypto_init0() 需要在 btl_storage_init() 前面使用
*/
int btl_crypto_init0(unsigned char key[16]);

typedef struct btl_crypto_params {
    int (*init)(void* ctx);
    int (*encrypt)(void* ctx, unsigned char* data, unsigned int len);
    int (*decrypt)(void* ctx, unsigned char* data, unsigned int len);
} btl_crypto_t;

/* @name    btl_crypto_init1
 * @brief   初始化客户自定义加密算法
 * @param   ctx                  :  [in] 传入客户自定义加密算法上下文ctx
 * @param   params               :  [in] 加密算法相应API
 * @example
          struct __xxx_crypto_type {  // 客户自定义算法上下文结构体
              unsigned char key[key_length];
              // other paramters with algorithm
          };

          int __xxx_crypto_init(void *ctx) 
          {
                // TODO algorithm init 
                if (sucessful)
                    return 0;
                else 
                    return -1;
          }

          int __xxx_crypto_encrypt(void* ctx, unsigned char* data, unsigned int len)
          {
            // TODO encrypt data
            if (sucessful)
                return 0;
            else 
                return -1;
          }

          int __xxx_crypto_decrypt(void* ctx, unsigned char* data, unsigned int len)
          {
                // TODO decrypt data
                if (sucessful)
                    return 0;
                else 
                    return -1;
          }

          struct __xxx_crypto_type __test_demo_ctx = {0};
          struct btl_crypto_params params = {0};
          params.init = __xxx_crypto_init;  // 如果客户算法没有初始化得可以将此置为NULL,
          params.encrypt = __xxx_crypto_encrypt;
          params.decrypt = __xxx_crypto_decrypt;
          btl_crypto_init1((void*)&__test_demo_ctx, &params);
 * 
 * @note    1. btl_crypto_init1()内部会保持ctx指针(并不会改变)，并在需要时传递给 init/encrypt/decrypt 回调函数
 * @note    2. init 回调函数可以为 NULL，如果不为空则 btl_crypto_init 函数内部会调用一次init回调函数
 * @note    3. encrypt 回调函数,或者 decrypt 回调函数不为空，否则加/解密功能不使能
 * @note    4. 模板数据写入flash前会调用 encrypt 回调函数来加密数据，encrypt 回调函数参数 data 为IN/OUT类型, 客户可直接操作此buffer
 * @note    5. 模板数据读取flash前会调用 decrypt 回调函数来解密数据，decrypt 回调函数参数 data 为IN/OUT类型, 客户可直接操作此buffer
 * @note    6. btl_crypto_init1() 需要在 btl_storage_init() 前面使用
 */
int btl_crypto_init1(void* ctx, btl_crypto_t* params);

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/***********************************************************************
 * @name    btl_storage_external_convert_to_flash_read_addr
 * @brief   flash的写地址转换成读地址

 * @param   flash_write_addr      :  [in] flash写地址
 * @retval  unsigned int    ： [out] 转换成flash读地址
 * @example
    unsigned int customer_convert_to_flash_read_addr(unsigned int flash_write_addr)
    {
        return flash_write_addr+XIP_ADDR_OFFSET;
    }
    //btl_storage_init初始化时赋值
    storage_setting.convert_to_flash_read_addr = customer_convert_to_flash_read_addr;
 **********************************************************************/
typedef unsigned int (*btl_storage_external_convert_to_flash_read_addr)(unsigned int flash_write_addr);

/***********************************************************************
 * @name    btl_storage_external_jump_flash_addr
 * @brief   flash地址转换，根据传入的flash地址，转换成实际的flash地址，用于模板存储区域不连续的应用场景，
如一部分模板放在内部flash，一部分模板放在外部flash。如果模板存储区域是连续的，则不需要实现这个接口。

 * @param   flash_addr      :  [in] 要读的flash地址
 * @retval  unsigned int    ： [out] 转换成实际的flash地址
 * @example
        //最大可注册的模板数量
        #define MAX_FP_TEMPLATE_COUNT         50
        //存放在内部flash的模板数量
        #define INTERNAL_FP_TEMPLATE_COUNT    10
        //一枚指纹的模板容量
        #define FP_TEMPLATE_SIZE              (16*1024)
        //指纹模板存在内部Flash的起始地址
        #define INTERNAR_FLASH_FP_ADDR_START  0x40000
        //指纹模板存在内部Flash的结束地址
        #define INTERNAR_FLASH_FP_ADDR_END    (INTERNAR_FLASH_FP_ADDR_START+FP_TEMPLATE_SIZE*INTERNAL_FP_TEMPLATE_COUNT)
        //指纹模板存在外部Flash的起始地址
        #define EXTERNAR_FLASH_FP_ADDR_START  0x60000

        unsigned int customer_jump_flash_addr(unsigned int flash_addr)
        {
            if(flash_addr >= INTERNAR_FLASH_FP_ADDR_END)
            {
                return flash_addr+(EXTERNAR_FLASH_FP_ADDR_START-INTERNAR_FLASH_FP_ADDR_END);
            }else{
                return flash_addr;
            }
        }
 **********************************************************************/
typedef unsigned int (*btl_storage_external_jump_flash_addr)(unsigned int flash_addr);

/***********************************************************************
 * @name    btl_storage_external_invalidate_flash_data
 * @brief   使flash中的数据失效，使用者需要实现的接口。如果擦除比较费时，那么写乱数据也是可以的(如写00)

 * @param   flash_addr  :  [in] 要擦除的flash起始地址
 * @param   len         :  [in] 擦除的长度
 * @retval  int         ： [out] 错误码，0表示成功
 * @example
    int customer_invalidate_flash_data(unsigned int flash_addr, unsigned int len)
    {
      int retval = BSEC_OK;
      int i = 0;
      int min_invalidate_data_len = MAX(sizeof(btl_storage_head_t),256); //256是flash的最小写长度
      unsigned char* invalidate_data = NULL;

      if(len < min_invalidate_data_len)
      {
        return BSEC_WRITE_FLASH_FAIL;
      }

      invalidate_data = (unsigned char*)malloc(min_invalidate_data_len);
      memset(invalidate_data,0,min_invalidate_data_len);//flash擦除后数据是0xff,故写0可破坏原来的数据

      for(i = 0; i<len; i+=FP_TEMPLATE_SIZE) //破坏每个模板数据头即可
      {
        retval = write_data_to_flash(flash_addr+i, invalidate_data, min_invalidate_data_len);
        if(BSEC_OK!=retval)
        {
          break;
        }
      }

      free(invalidate_data);
      invalidate_data = NULL;

      return retval;
    }
 **********************************************************************/
typedef int (*btl_storage_external_invalidate_flash_data)(unsigned int flash_addr, unsigned int len);

/***********************************************************************
 * @name    btl_storage_external_save_data_to_flash
 * @brief   写数据到flash中，使用者需要实现的接口。
 使用者需要确保数据写成功，包括写之前检查是否为空，否则要先擦掉再写，写后校验。

 * @param   flash_addr  :  [in] 要写的flash起始地址
 * @param   data        :  [in] 要写的数据
 * @param   len         :  [in] 数据长度
 * @retval  int         ： [out] 错误码，0表示成功
 * @example
        int customer_save_data_to_flash(unsigned int flash_addr, const unsigned char* data, unsigned int len)
        {
            int retval = BSEC_OK;
            int i = 0;
            unsigned int read_flash_addr = 0;

            //先检查是否为空
            if(!is_flash_can_be_write(flash_addr, len))
            {
                //不为空，先擦除，这部分不可少
                retval = real_erase_flash(flash_addr, len);
                if(BSEC_OK != retval)
                { //擦除失败
                    return retval;
                }
            }

            //写入
            retval = write_data_to_flash(flash_addr, data, len);

            //回读校验，使用XIP方式读flash
            read_flash_addr = customer_convert_to_flash_read_addr(flash_addr);
            for(i=0; i<len; i++)
            {
                if(data[i] != *((unsigned char*)(read_flash_addr+i)))
                {
                    retval = BSEC_WRITE_FLASH_FAIL;
                    break;
                }
            }

            return retval;
        }
 **********************************************************************/
typedef int (*btl_storage_external_save_data_to_flash)(unsigned int flash_addr, const unsigned char* data, unsigned int len);

typedef struct btl_storage_template_info_st {
    unsigned char is_template_valid;  // 是否存在有效模板，0表示没有，1表示有
    unsigned short template_id;
    unsigned char template_type;
    unsigned int template_size;
    unsigned int template_data_addr_offset;  // 模板数据相对此模板存储的首地址的偏移量
} btl_storage_template_info_t;

/***********************************************************************
 * @name    btl_storage_decode_user_defined_template
 * @brief   为了使新的模板数据结构能够兼容用户自定义的旧的模板数据，用户需要实现此转换接口，如果是新项目，则不需要实现。
            必须在btl_storage_init之前赋值

 * @param   flash_addr          :  [in] flash起始地址
 * @param   len                 :  [in] 数据长度
 * @param   p_template_info     :  [out] 模板信息
 * @retval  int                 ： [out] 错误码，0表示成功
 * @example
    #define FP_MAGIC_VALUE              (0x1234)
    #define DEFAULT_VALUE_AFTER_ERASED  (0x0)

    struct fp_templates_t{
        struct fp_templates_t *next;
        int         id  : 16;
        int         magic : 16;
        BL_TEMPLATE template;
        uint8_t     data[0];
    };
    int customer_decode_user_defined_template(unsigned int flash_addr, btl_storage_template_info_t* p_template_info)
    {
        int retval = BSEC_OK;

        struct fp_templates_t *p;

        p = (struct fp_templates_t*)flash_addr;
        if(FP_MAGIC_VALUE == p->magic && p->id != DEFAULT_VALUE_AFTER_ERASED)
        {
            p_template_info->is_template_valid = 1;
            p_template_info->template_id = p->id;
            p_template_info->template_size = p->template.templateSize;
            p_template_info->template_type = p->template.templateType;
            p_template_info->template_data_addr_offset = sizeof(struct fp_templates_t);
        }else{
            p_template_info->is_template_valid = 0;
        }
        return retval;
    }
    //在btl_storage_init之前赋值
    btl_storage_decode_user_defined_template = customer_decode_user_defined_template;
 **********************************************************************/
extern int (*btl_storage_decode_user_defined_template)(unsigned int flash_addr, btl_storage_template_info_t* p_template_info);

/***********************************************************************
 * @name    btl_storage_is_xip_accessible
 * @brief   咨询相应flash地址是否可XIP读，必须在btl_storage_init之前赋值

 * @param   flash_addr          :  [in] flash起始地址
 * @param   len                 :  [in] 数据长度
 * @retval  int                 ： [out] 1表示可XIP读，0表示不可以
 * @example

    int customer_is_xip_accessible(unsigned int flash_addr, unsigned int len)
    {
        int retval = 1;

        if(允许条件)
        {
            retval = 1;
        }else{
            retval = 0;
        }
        return retval;
    }
    //在btl_storage_init之前赋值
    btl_storage_is_xip_accessible = customer_is_xip_accessible;
 **********************************************************************/
extern int (*btl_storage_is_xip_accessible)(unsigned int flash_addr, unsigned int len);

/***********************************************************************
 * @name    btl_storage_xip_read_callback
 * @brief   当flash支持XIP读时，需要提供回调函数，用于控制XIP使能
 * @param   xip_enable          :  [in] 1表示使能XIP，0表示关闭使能XIP
 * @retval  int                 ： 错误码，0表示成功
 * @example
    int customer_xip_read_callback(unsigned char xip_enable)
    {
        int retval = 0;

        if (xip_enable) {
            //  打开使能XIP
            readFlashXipOn();
        } else {
            // 关闭使能XIP
            readFlashXipOff();
        }
        return retval;
    }
  // 在btl_storage_init之前赋值
  btl_storage_xip_read_callback = customer_xip_read_callback;
*/
extern int (*btl_storage_xip_read_callback)(unsigned char xip_enable);

/***********************************************************************
 * @name    btl_storage_read_flash
 * @brief   当flash不支持XIP读时，从flash中读数据到ram中，因此需要同时实现btl_storage_is_xip_accessible。
            必须在btl_storage_init之前赋值

 * @param   flash_addr          :  [in] flash起始地址
 * @param   len                 :  [in] 数据长度
 * @param   ram_buffer          :  [in] RAM缓存地址，读到的数据放入其中
 * @retval  int                 ： [out] 错误码，0表示成功
 * @example

    int customer_read_flash(unsigned int flash_addr, unsigned int len, void* ram_buffer)
    {
        int retval = 0;
        //从flash_addr地址，读len长度的数据放到ram_buffer中
        return retval;
    }
    //在btl_storage_init之前赋值
    btl_storage_is_xip_accessible = customer_is_xip_accessible;
    btl_storage_read_flash = customer_read_flash;
 **********************************************************************/
extern int (*btl_storage_read_flash)(unsigned int flash_addr, unsigned int len, void* ram_buffer);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

typedef enum btl_storage_switch_flag_e {
    BSCF_NULL                                = 0,            // 无
    BSCF_ENABLE_FAKE_FINGER_DETECT           = (0x01 << 0),  // 使能假指纹检测，非必要不开启
    BSCF_LOCK_ENROLL_TEMPLATES               = (0x01 << 1),  // 锁定注册时的模板，不会被更新替换掉
    BSCF_VERIFY_DOUBLE_CHECK                 = (0x01 << 2),  // 是否对比对结果进行二次认证，防自学习攻击
    BSCF_COMPLETE_ENROLL_TEMPLATE_FIRST      = (0x01 << 3),  // 自学习时首先完善注册模板
    BSCF_REVERIFY_WITH_HIGHER_SECURITY_LEVEL = (0x01 << 4),  // 对match的ID使用更高的安全等级(+2)比对，前置条件是此ID的模板已满容量
    BSCF_VERIFY_LAST_VIP_ID_FIRST            = (0x01 << 5),  // 优先比对最近一枚VIP模板
} btl_storage_switch_flag_t;

typedef struct btl_storage_setting_st {
    // 读和写地址不一致时需要转换，否则传NULL即可
    btl_storage_external_convert_to_flash_read_addr convert_to_flash_read_addr;
    // 模板分段存储时(如一部分在内部flash，一部分在外部flash)需要转换，否则传NULL即可
    btl_storage_external_jump_flash_addr jump_flash_addr;
    // 使flash数据无效，必须要实现的接口
    btl_storage_external_invalidate_flash_data external_invalidate_flash_data;
    // 写flash数据，必须要实现的接口
    btl_storage_external_save_data_to_flash external_write_flash;
    // 模板存储flash首地址，支持从0开始
    unsigned int base_flash_addr_for_template;
    // 每个模板占用空间大小（字节），
    // 总的模板空间=storage_size_per_template*(define_max_nbr_of_templates+added_nbr_of_templates)
    unsigned int storage_size_per_template;
    // 定义的最大支持指纹数量
    unsigned short define_max_nbr_of_templates;
    // 额外增加的模板存储数量，仅用于存放自学习的模板
    unsigned short added_nbr_of_templates;
    // 用于放其它信息的flash首地址，支持从0开始
    unsigned int base_flash_addr_for_other_info;
    // 用于放其它信息的flash空间大小，需求在100字节以内
    unsigned int flash_size_for_other_info;
    // 一次性可比对的最大模板数量。默认0，表示没有限制，当RAM不足时，可设置成非0，分批比对
    unsigned char max_verify_count_one_time;
    // 模板ID起始编号，默认是从0开始
    unsigned short template_id_start_number;
    // 一些配置信息
    btl_storage_switch_flag_t switch_flag;
} btl_storage_setting_t;

typedef enum btl_storage_head_config_param_e {
    BSHCP_PURE_UPDATE_TEMPLATE = (0x01 << 0),  // 纯更新模板
    BSHCP_MALLOC_HEADER = (0x01 << 1),  // 模板数据头存放于malloc的ram中，需要free
    BSHCP_ENCRYP_HEADER = (0x01 << 2),  /* 模板数据头加密后16-bytes*/
} btl_storage_head_config_param_t;

/*
模板头结构体，占用20字节
5A A1 00 00 04 00 FF 7F 00 00 00 00 50 00 00 00 5E 2B 00 00
-- -- -- -- ----- ----- ----- ----- -- -- ----- -----------
 |  |  |  |   |     |     |     |   |   |   |       |__ template_size
 |  |  |  |   |     |     |     |   |   |   |__________ crc16_template_data
 |  |  |  |   |     |     |     |   |   |______________ remain_space
 |  |  |  |   |     |     |     |   |__________________ template_type
 |  |  |  |   |     |     |     |______________________ template_id
 |  |  |  |   |     |     |____________________________ crc16_section
 |  |  |  |   |     |__________________________________ max_cap_value
 |  |  |  |   |________________________________________ update_times
 |  |  |  |____________________________________________ config_param
 |  |  |_______________________________________________ version
 |  |__________________________________________________ bcc_check_sum
 |_____________________________________________________ flag
*/
typedef struct btl_storage_head_st {
    unsigned char flag;
    unsigned char bcc_check_sum;         // 首尾部分数据的异或检验
    unsigned char version;               // 数据结构版本号
    unsigned char config_param;          // 一些配置信息，见枚举btl_storage_head_config_param_t
    unsigned short update_times;         // 模板更新次数，首次注册的模板为0
    signed short max_cap_value;          // 注册指纹的电容值最大值
    unsigned short crc16_section;        // 首尾部分数据的CRC16校验
    unsigned short template_id;          // 模板ID
    unsigned char template_type;         // 模板类型
    unsigned char remain_space;          // 预留空间
    unsigned short crc16_template_data;  // 所有template data的crc16校验
    unsigned int template_size;
} btl_storage_head_t;

/***********************************************************************
 * @name    btl_storage_init
 * @brief   模板存储信息初始化，此接口会读flash，耗时约2ms（200MHz）

 * @param   storage_setting        :  [in]需要赋值的结构体
 * @retval  BTL_STORAGE_ERR_CODE   ：
 * @example
        //flash一个扇区的大小
        #define FLASH_SECTOR_SIZE             4096
        //用于存放额外信息的空间首地址
        #define OTHER_INFO_BASE_ADDR          0x40000
        //最大可注册的模板数量
        #define MAX_FP_TEMPLATE_COUNT         50
        //额外增加的模板数量
        #define ADDED_FP_TEMPLATE_COUNT       1
        //一枚指纹的模板容量
        #define FP_TEMPLATE_SIZE              (16*1024)
        //指纹模板存在内部Flash的起始地址
        #define FP_TEMPLATE_BASE_ADDR         (OTHER_INFO_BASE_ADDR+FLASH_SECTOR_SIZE)
        //总（额外信息+所有模板）的空间占用 = FLASH_SECTOR_SIZE + FP_TEMPLATE_SIZE*(MAX_FP_TEMPLATE_COUNT+ADDED_FP_TEMPLATE_COUNT)

        btl_storage_setting_t storage_setting = {0};

        storage_setting.convert_to_flash_read_addr = NULL; //读和写地址一致，不需要转换
        storage_setting.jump_flash_addr = NULL; //地址空间连续，不需要转换
        storage_setting.external_invalidate_flash_data = customer_invalidate_flash_data;
        storage_setting.external_write_flash = customer_save_data_to_flash;

        storage_setting.base_flash_addr_for_template = FP_TEMPLATE_BASE_ADDR;
        storage_setting.storage_size_per_template = FP_TEMPLATE_SIZE;
        storage_setting.define_max_nbr_of_templates = MAX_FP_TEMPLATE_COUNT;
        storage_setting.added_nbr_of_templates = ADDED_FP_TEMPLATE_COUNT;
        storage_setting.max_verify_count_one_time = 0; //RAM足够大，不需要分批比对

        storage_setting.base_flash_addr_for_other_info = OTHER_INFO_BASE_ADDR;
        storage_setting.flash_size_for_other_info = FLASH_SECTOR_SIZE;

        storage_setting.template_id_start_number = 0;
        storage_setting.switch_flag = BSCF_LOCK_ENROLL_TEMPLATES|BSCF_COMPLETE_ENROLL_TEMPLATE_FIRST;
        if(BSEC_OK != btl_storage_init(storage_setting))
        {
            //初始化失败
        }
        btl_storage_set_max_vip_count(5);
 **********************************************************************/
/* XIP READ */
extern int btl_storage_init(btl_storage_setting_t storage_setting);

/*
资源释放，在MCU休眠之后RAM没法保持的情况下需要调用
*/
extern int btl_storage_uninit(void);
/*
获取已注册模板的个数
*/
extern unsigned int btl_storage_get_enroll_template_count(void);
/*
获取所有模板的个数，包含注册的模板和更新的模板
*/
extern unsigned int btl_storage_get_all_template_count(void);
/*
为新的注册模板获取可用ID，从template_id_start_number开始
*/
extern int btl_storage_get_new_id_for_enroll_template(unsigned short* template_id);

/***********************************************************************
 * @name    btl_storage_get_enroll_template_id_array
 * @brief   获取已注册ID的列表

 * @param   id_array        :  [out]内部申请空间，需要外部释放
 * @param   count           :  [out]返回模板数量
 * @retval  BTL_STORAGE_ERR_CODE    ：
 * @example
        unsigned short* id_array = NULL;
        unsigned int count = 0;

        btl_storage_get_enroll_template_id_array(&id_array, &count);
        if(id_array)
        {
            //获取成功

            free(id_array);
            id_array = NULL;
        }else{
            //不存在

        }
 **********************************************************************/
extern int btl_storage_get_enroll_template_id_array(unsigned short** id_array, unsigned int* count);
/***********************************************************************
 * @name    btl_storage_set_max_vip_count
 * @brief   设置最大允许的VIP数量，VIP ID可以优先比对，刚注册和最近比对成功的指纹会被设置成VIP，最多20个

 * @param   count        :  [in]最大允许VIP数量，不可以超过20, 默认值是0，即没有VIP
 * @retval  BTL_STORAGE_ERR_CODE    ：
 * @example

 **********************************************************************/
extern int btl_storage_set_max_vip_count(unsigned char count);
/***********************************************************************
 * @name    btl_storage_get_vip_template_id_array
 * @brief   获取VIP ID的列表，前提是调用btl_storage_set_max_vip_count设置了VIP数量

 * @param   id_array        :  [out]内部申请空间，需要外部释放
 * @param   count           :  [out]返回模板数量
 * @retval  BTL_STORAGE_ERR_CODE    ：
 * @example
        unsigned short* id_array = NULL;
        unsigned int count = 0;

        btl_storage_get_vip_template_id_array(&id_array, &count);
        if(id_array)
        {
            //获取成功

            free(id_array); //释放内存
            id_array = NULL;
        }else{
            //不存在
        }
 **********************************************************************/
extern int btl_storage_get_vip_template_id_array(unsigned short** id_array, unsigned int* count);

/*
删除指定ID的模板
*/
extern int btl_storage_remove_template(unsigned short template_id);

/*
删除所有的模板
*/
/* XIP READ */
extern int btl_storage_remove_all_template(void);
/***********************************************************************
 * @name    btl_storage_create_all_templates_mr
 * @brief   获取库中的所有模板，包括注册模板和更新模板，注册模板放在前，更新模板放在后。
 用完注意调用btl_storage_delete_templates释放

 * @param   pblMultiTemplates        :  [out]返回模板数据
 * @param   nNbrOfMultiTemplates     :  [out]返回模板数量
 * @retval  BTL_STORAGE_ERR_CODE    ：
 * @example
     PBL_TEMPLATE* pblMultiTemplatesArray = NULL;
     unsigned int nNbrOfMultiTemplates = 0;
     int index = 0, enroll_template_index = -1, update_template_index = -1;
     btl_storage_create_all_templates_mr(&pblMultiTemplatesArray, &nNbrOfMultiTemplates);
     if(nNbrOfMultiTemplates > 0)
     {//获取模板成功
            for(index = 0; index < nNbrOfMultiTemplates; index++)
            {
                if(find_template_id == pblMultiTemplatesArray[index]->template_id){ //找到需要的ID
                    if(-1 == enroll_template_index){
                        enroll_template_index = index;
                    }else if(-1 == update_template_index){
                        update_template_index = index;
                        break; //注册和更新模板皆找到
                    }
                }
            }
     }
     if(enroll_template_index > -1){ //找到注册模板
         //使用注册模板
     }
     if(update_template_index > -1){ //找到更新模板
         //使用更新模板
     }
     //释放资源
     btl_storage_delete_templates(&pblMultiTemplatesArray, nNbrOfMultiTemplates);
 **********************************************************************/
/* XIP READ */
extern int btl_storage_create_all_templates_mr(PBL_TEMPLATE** pblMultiTemplates, unsigned int* nNbrOfMultiTemplates);
/*
释放模板占用的资源
*/
extern int btl_storage_delete_templates(PBL_TEMPLATE** pblMultiTemplates, unsigned int nNbrOfMultiTemplates);

/***********************************************************************
 * @name    btl_storage_save_template
 * @brief   保存多模板到flash中

 * @param   pblMultiTemplate        :  [in]模板数据
 * @param   is_enroll               :  [in]是否为注册模板，1表示是，0表示属于更新模板
 * @retval  BTL_STORAGE_ERR_CODE    ：
 * @example
     unsigned short template_id = 0;
     if(BSEC_OK == btl_storage_get_new_id_for_enroll_template(&template_id))
     {
            //获取模板ID成功
            pblMultiTemplate->template_id = template_id; //记得给模板ID赋值哦
            btl_storage_save_template(pblMultiTemplate,1);
     }
 **********************************************************************/
/* XIP READ */
extern int btl_storage_save_template(const PBL_TEMPLATE pblMultiTemplate, unsigned char is_enroll);

/////////////////////////////////////////////////////////////////////////////////////////////////////
/***********************************************************************
 * @name    btl_ai_verify
 * @brief   使用当前模板跟库中的模板库进行智能比对，返回比对成功的模板ID

 * @param   match_template_id    :  [out]比中的模板ID,如果比对失败，则返回-1
 * @retval  btl_rc_t    ：
 **********************************************************************/
/* XIP READ */
extern btl_rc_t btl_ai_verify(int* match_template_id);

///***********************************************************************
// * @name    btl_ai_verify_speed
// * @brief   快速比对，用于注册时候的查重
//
// * @param   nMaxNbrOfSubTempaltes:  [in]只比对每个多模板中的前nMaxNbrOfSubTempaltes个子模板
// * @param   match_template_id    :  [out]比中的模板ID,如果比对失败，则返回-1
// * @retval  btl_rc_t    ：
// **********************************************************************/
// extern btl_rc_t btl_ai_verify_speed(unsigned char  nMaxNbrOfSubTempaltes, int *match_template_id);

/***********************************************************************
 * @name    btl_ai_specify_verify
 * @brief   当前模板跟库中的某个指定模板进行比对

 * @param   specify_template_id    :  [in]指定ID
 * @param   is_matched             :  [out]比对结果，0表示失败，1表示成功
 * @retval  btl_rc_t    ：
 **********************************************************************/
/* XIP READ */
extern btl_rc_t btl_ai_specify_verify(unsigned short specify_template_id, unsigned char* is_matched);

/***********************************************************************
 * @name    btl_ai_group_verify
 * @brief   当前模板跟库中的某组模板进行比对

 * @param   template_id_group      :  [in]指定的模板ID数组
 * @param   group_size             :  [in]数组大小
 * @param   match_template_id      :  [out]比中的模板ID,如果比对失败，则返回-1
 * @retval  btl_rc_t    ：
 **********************************************************************/
extern btl_rc_t btl_ai_group_verify(unsigned short* template_id_group, int group_size, int* match_template_id);

typedef enum btl_storage_update_type_e {
    BSUT_UPDATE_TEMPLATE   = (0x01 << 0),  // 更新模板
    BSUT_UPDATE_OTHER_INFO = (0x01 << 1),  // 更新其它信息，如VIP
    BSUT_REMOVE_VIP        = (0x01 << 2),  // 从VIP列表中移除该ID，此bit为0则表示添加VIP，为1则表示移除VIP
} btl_storage_update_type_t;

/***********************************************************************
 * @name    btl_ai_update
 * @brief   使用当前模板跟库中的指定模板进行融合，并自动保存模板。
 使用本接口自学习时，BTL_PARAM中的lock_templates_after_enroll要设置成0.
 是否锁定注册模板，由枚举BTL_STORAGE_CONFIG_FLAG中的BSCF_LOCK_ENROLL_TEMPLATES决定

 * @param   match_template_id    :  [in]库中模板的ID
 * @param   update_type          :  [in]更新项
 * @retval  btl_rc_t    ：
 * @example
//注册完成后，设置该ID为VIP
btl_ai_update(enroll_template_id, BSUT_UPDATE_OTHER_INFO);
//比对成功后，更新模板，并设置该ID为VIP
btl_ai_update(match_template_id, BSUT_UPDATE_TEMPLATE|BSUT_UPDATE_OTHER_INFO);
//从VIP列表中移除某个ID
btl_ai_update(match_template_id, BSUT_UPDATE_OTHER_INFO|BSUT_REMOVE_VIP);
 **********************************************************************/
/* XIP READ */
extern btl_rc_t btl_ai_update(int match_template_id, btl_storage_update_type_t update_type);

typedef struct btl_storage_enroll_setting_st {
    /*
    是否拒绝同个手指重复注册，0(默认值)表示不拒绝，1表示拒绝。
    此参数只对btl_ai_enroll接口有效，在设置为1的情况下，出现重复的话返回BTL_RC_EXIST_ENROLL_ID
    */
    unsigned char reject_duplacate_finger;
    /*
    最后一次录入完成后，获取每个子模板(或合成模板)和其它子模板之间的相似分，
    统计相似分大于安全阈值的个数，如果小于此设定值则注册失败，默认值是0
    */
    unsigned char min_matched_count;
    /*
    安全阈值设定，默认值btl_get_config()->nFarAccepted
    */
    unsigned char matched_far_accepted;
    /*
    选择缓存区中最优的几个子模板合成为多模板,默认值是0，表示不挑选，全部合成。
    此参数只对btl_ai_create_mutiltemplate接口有效
    */
    unsigned char nbr_of_subtemplates_to_choose;
    /*
    择优注册后，剩余子模板是否保留，如果保留，则作为自学习模板保存。默认值是0，表示不保留。
    此参数只对btl_ai_create_mutiltemplate接口有效
    */
    unsigned char is_keep_remain_subtemplates;
} btl_storage_enroll_setting_t;

/***********************************************************************
 * @name    btl_storage_get_default_enroll_setting
 * @brief   获取默认的注册设置
 * @example
 **********************************************************************/
extern btl_storage_enroll_setting_t btl_storage_get_default_enroll_setting(void);

/***********************************************************************
 * @name    btl_ai_enroll
 * @brief   智能注册，注册完成自动保存模板。注册中途如果要取消，请切换到其它模式，如btl_set_work_mode(BTL_FINGER_DETECT_MODE);

  * @param   template_id     :  [in]模板ID
  * @param   pEnrollSetting  :  [in]录入设置，传入NULL表示使用默认设置
  * @param   pAcceptedNum    :  [out]返回已接受次数
  * @param   island_num      :  [out] 孤岛数，非必须，只有prevent_enroll_multifingers为非0的时候才会返回
  * @param   is_finish       :  [out]返回是否已经注册完成，0表示未完成，1表示已经完成
  * @retval  btl_rc_t    ：
  * @example
    //获取可用ID
    btl_storage_get_new_id_for_enroll_template(&template_id);
    //获取默认注册设置
    btl_storage_enroll_setting_t enroll_setting = btl_storage_get_default_enroll_setting();
    //注册
    retval = btl_ai_enroll(template_id, &enroll_setting, &accepted_num,NULL,&is_finish_enroll);
    if(BTL_RC_OK == retval)
    {
        if(is_finish_enroll)
        {
            //注册完成
            btl_set_work_mode(BTL_FINGER_DETECT_MODE);
        }
        else//注册中
        {
            //已注册成功次数为accepted_num

        }
    }else{
        //本次注册失败，提示抬起手指重新按压
    }
 **********************************************************************/
/* XIP READ */
extern btl_rc_t btl_ai_enroll(unsigned short template_id, btl_storage_enroll_setting_t* pEnrollSetting, int* pAcceptedNum, int* island_num, unsigned char* is_finish);

////////////////////////////////////////以下接口用于分步式注册////////////////////////////////////////////////
/*
对于晟元协议，由于无法区分采图是用于注册或比对，因此建议统一使用比对模式采图，
然后提取模板时调用btl_ai_add_subtemplate来提取并缓存当前模板，
如果是第一次缓存，则不需要判断这个接口的返回值（统一认为是成功的），从第二次开始才判断返回值。
*/
/***********************************************************************
 * @name    btl_ai_add_subtemplate
 * @brief   缓存当前子模板，注册中途如果要取消，请调用btl_ai_delete_all_subtemplate来清空缓冲区。

 * @param   nbr_of_subtemplate    :  [out]返回缓存区中所有子模板的个数
 * @retval  btl_rc_t    ：
 * @example
    if(pUartBuffer[10] == 1) // first
    {
        btl_ai_delete_all_subtemplate(); // 清空buffer
        btl_ai_add_subtemplate(&num);
        retval = BTL_RC_OK;              // 第一次都认为成功
    }else{
        retval = btl_ai_add_subtemplate(&num);
    }
    if (BTL_RC_OK == retval) {
        //成功
    } else {
        //失败
    }
 **********************************************************************/
extern btl_rc_t btl_ai_add_subtemplate(int* nbr_of_subtemplate);

/***********************************************************************
 * @name    btl_ai_delete_last_subtemplate
 * @brief   删除缓存区中最近一次添加的子模板

 * @param   remain_nbr_of_subtemplate    :  [out]返回缓存区中剩余子模板的个数
 * @retval  btl_rc_t    ：
 **********************************************************************/
extern void btl_ai_delete_last_subtemplate(int* remain_nbr_of_subtemplate);

/***********************************************************************
 * @name    btl_ai_get_last_subtemplate
 * @brief   获取缓存区中最近一次添加的子模板

 * @param   pLastSubTemplate    :  [out]返回模板数据，需要使用者释放
 * @retval  int    ： 错误码
 * @example
  BL_TEMPLATE SubTemplate = {0};
  if(BSEC_OK == btl_ai_get_last_subtemplate(&SubTemplate))
  {
    btl_delete_template(&SubTemplate);
  }
 **********************************************************************/
extern int btl_ai_get_last_subtemplate(BL_TEMPLATE* pLastSubTemplate);

/***********************************************************************
 * @name    btl_ai_get_specified_subtemplate
 * @brief   获取缓存区中指定的子模板

 * @param   num                 :  [in]从1开始，1代表最后一个加入的子模板，2表示倒数第2个加入的子模板，以此类推
 * @param   pLastSubTemplate    :  [out]返回模板数据，需要使用者释放
 * @retval  int    ： 错误码
 * @example
  BL_TEMPLATE SubTemplate = {0};
  if(BSEC_OK == btl_ai_get_specified_subtemplate(1,&SubTemplate))
  {
    btl_delete_template(&SubTemplate);
  }
 **********************************************************************/
extern int btl_ai_get_specified_subtemplate(int num, BL_TEMPLATE* pSubTemplate);

/***********************************************************************
 * @name    btl_ai_delete_all_subtemplate
 * @brief   删除缓存区中的所有子模板

 * @param   nbr_of_subtemplate    :  [out]返回缓存区中所有子模板的个数
 * @retval  btl_rc_t    ：
 **********************************************************************/
extern void btl_ai_delete_all_subtemplate(void);

/***********************************************************************
 * @name    btl_ai_get_subtemplate_count
 * @brief   获取缓存区中的所有子模板的个数

 * @retval  unsigned int    ： 个数
 **********************************************************************/
extern unsigned int btl_ai_get_subtemplate_count(void);

/***********************************************************************
 * @name    btl_ai_create_mutiltemplate
 * @brief   把缓存区中的所有子模板合成为多模板并输出，合成完之后自动删除所有子模板

 * @param   pbtlMultiTemplates    :  [in/out]合成的多模板
 * @retval  btl_rc_t    ：
 * @example
  btl_rc_t retval = BTL_RC_OK;
  BL_TEMPLATE blMutilTemplate = {0};
  retval = btl_ai_create_mutiltemplate_ex(&blMutilTemplate);
  if(blMutilTemplate.pTemplateData && blMutilTemplate.templateSize > 0)
  {
    //写入flash
    btl_storage_save_template(&blMutilTemplate, 1);
  }
  btl_delete_template(&blMutilTemplate);
 **********************************************************************/
extern btl_rc_t btl_ai_create_mutiltemplate_ex(PBL_TEMPLATE pbtlMultiTemplates);

/***********************************************************************
 * @name    btl_ai_create_mutiltemplate
 * @brief   把缓存区中的所有子模板合成为多模板并保存，合成完之后自动删除所有子模板

 * @param   template_id    :  [in]传入多模板的ID
 * @param   pEnrollSetting :  [in]录入设置，传入NULL表示使用默认设置
 * @retval  btl_rc_t    ：
 **********************************************************************/
extern btl_rc_t btl_ai_create_mutiltemplate(unsigned short template_id, btl_storage_enroll_setting_t* pEnrollSetting);

///***********************************************************************
// * @name    btl_ai_create_mutiltemplate_choose 使用btl_ai_create_mutiltemplate替代,参数.nbr_of_subtemplates_to_choose设成非0;
// * @brief   选择缓存区中最优的几个子模板合成为多模板并保存，剩余的子模板将作为自学习模板保存，合成完之后自动删除所有子模板
//
// * @param   template_id    :  [in]传入多模板的ID
// * @param   choose_count   :  [in]选择的子模板个数
// * @retval  btl_rc_t    ：
// **********************************************************************/
// extern btl_rc_t btl_ai_create_mutiltemplate_choose(unsigned short template_id, unsigned char choose_count);

////////////////////////////////////////以上接口用于分步式注册////////////////////////////////////////////////

#endif
